This tutorial reinforces concepts on presentations of chrome extension, how to sendMessage, how to receive message at onMessage, how background.js works, and how Storage API works. It’s more of a guide then providing you the complete solutions, because you have to struggle a bit to learn. However if you give up, the solution is at the bottom. ## Add Options presentation create icon sets as usual. you will create manifest.json that enables options page. then adjust the manifest.json as you add on more presentations in later steps (sidebar on context menu item, popup, DevTools panel, DevTools sidebar) options 1a. Create options page and save options with storage API DO: name the storage key as “opt” ![[Pasted image 20250406190731.png]] CHECKPOINT-WISE, folder structure should look like: ``` . ├── icon.png ├── icon128x128.png ├── icon16x16.png ├── icon32x32.png ├── icon48x48.png ├── manifest.json ├── options.css ├── options.html └── options.js ``` ## Create side panel that opens from context menu item Hint: [[3. Create sidepanel from context menu (Bonus - sidePanel.setOptions)]] Hint: add sidepanel and background to manifest.json. add sidePanel and contextMenus permission ^without contextMenus permission, the chrome extension will install with an error instantly, complaining that `Cannot read properties of undefined (reading 'onClicked')`  for background where the contextMenu onClick is at because that event handler will check if it’s the correct context menu item, and if it is correct, then we open the side panel. DO: name the storage key as “sp”. Checkbox item just like options.html (You can even copy the code and adjust js filename) CHECKPOINT-WISE, folder structure should look like: ``` . ├── background.js ├── icon.png ├── icon128x128.png ├── icon16x16.png ├── icon32x32.png ├── icon48x48.png ├── manifest.json ├── options.css ├── options.html ├── options.js ├── sidepanel.html └── sidepanel.js ``` Update the chrome extension and test the side panel. A/B View: ![[Pasted image 20250406190831.png]] B/B View: ![[Pasted image 20250406190852.png]] --- ## Create DevTools panel Hint: [[7. Walkthrough - DevToolsPanel with Inspected Window to Get Content Resources]] .. except dont gotta implement popup.html / popup.css. ...Dont do anything with that old tutorial’s background.js because you aren’t listening for a message to console log js/css resources of the web content (that tutorial’s goal). ...For devtools.js, just copy creating the panel (` chrome.devtools.panels.create) and make sure to the name the tab something appropriate like “Check-in” instead of “Get Resources” . nothing else should be copied because we aren’t dealing with js/css resources of the web content Your entire devtools.js might end up looking like this: ``` let panelWindow = null; chrome.devtools.panels.create("Check-in", "icon.png", "devt-panel.html", panel => { // Code invoked on panel creation panel.onShown.addListener((window) => { panelWindow = window; }); // shown panel }); ``` ...however the devt-panel.html should have options for checking in like the other presentations have. and it’ll need js file, perhaps devt-panel.html to handle the logic of saving the checkbox ticking to storage, just like the previous presentations have the same handling of checkboxes. Perhaps you can model it after options.html or sidepane.html Your devt-panel.html could end up looking like: ``` Options

Options

``` Hint: We add "devtools.html" entry point at manifest.json DO: Name the storage key as “dtp” CHECKPOINT-WISE, folder structure should look like: ``` . ├── background.js ├── devt-panel.html ├── devt-panel.js ├── devtools.html ├── devtools.js ├── icon.png ├── icon128x128.png ├── icon16x16.png ├── icon32x32.png ├── icon48x48.png ├── manifest.json ├── options.css ├── options.html ├── options.js ├── sidepanel.html └── sidepanel.js ``` Could look like this: ![[Pasted image 20250406190957.png]] --- ## Create DevTools sidebar Add a devtools sidebar (a sidebar pane for Elements tab). Hint it’s at [[5. Walkthrough - DevTools Panel and DevTools Sidebar Pane]] particularly at `#### init-devtools-sidebar.js:`  which is the code to initiate the sidebar. Place that into your devtools.js (at either the very top or very bottom, doesn’t matter). Do rename its tab to like, “Check-in”. So move this partial snippet into background.js ``` let sidebar = null; // Initialize the sidebar for the Elements panel chrome.devtools.panels.elements.createSidebarPane("Check-in", (mySidebar) => { // Set the HTML file for the sidebar mySidebar.setPage('devt-sidebar.html'); // Code invoked on panel creation mySidebar.onShown.addListener((sidebarWindow) => { console.log("DevTools_Sidebar is shown"); sidebar = sidebarWindow; }); // Log when the sidebar is hidden mySidebar.onHidden.addListener(() => { console.log("DevTools_Sidebar is hidden"); }); }); ``` Create the devt-sidebar.html that is the html for the sidebar. And create its js file for saving to strage api. Hints: check devt-panel’s, or option’s, or sidepanel’s. User clicks checkbox at devt-sidebar.html at the DevTools sidebar, then its js file (devt-sidebar.js) saves checkbox tick status into Storage. DO: Name the storage key as “dvtsb” Could look like this: ![[Pasted image 20250406191021.png]] --- ## Create popup that triggers background to write check-in report to content Let’s add popup. That’s to manifest action. default_icon and action.default_popup If forgot how to add a popup action, refer to “Popup Action” section at: [[2a. Chrome extension foundational with popup]] Your popup.html will be different: ``` Popup

Popup

``` Your popup.js: - Clicking the button would emit message “add-live-report” since the popup.js itself shouldn’t be the one adding the live report to content.js EVEN THOUGH it’s perfectly capabale of ``` document.querySelector("#add-live-report").addEventListener("click", () => { chrome.runtime.sendMessage({ action: "add-live-report" }); }); // Close the page document.getElementById('btn-close').addEventListener('click', async () => { window.close(); }); ``` The popup.css for popup.html is a bit different from the other checkbox ticking pages: - Mostly to make the popup appear in a reasonable width when clicking the chrome extension icon (`width: 300px`): ``` body { width: 300px; height: 130px; padding: 10px; } h1 { font-size: 20px; margin-bottom: 20px; } footer { position: absolute; bottom: 20px; right: 20px; text-align: right; } ``` Add “scripting” permission to manifest.json because we’ll have background.js execute script of writing to the web content at the current active tab. At background.js, add this code at the end: - on receiving message to add-live-report, background.js adds to the web content at the top (`prepend`) a live reporting of which pages have been checked in - remember we are doing messaging so that background does all the writing to the web content, which is best practice. You could have had popup.js write directly to web content (`chrome.tab.query({active: true.....}`) without needing to check the request.action or onMessage line. - **ADD (not change entire file) to background.js:** ``` function updateLiveReport(){ chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { const tabId = tabs[0].id; chrome.scripting.executeScript({ target: { tabId }, func: async () => { if(document.getElementById("ce-live-report")){ document.getElementById("ce-live-report").remove(); } const reportCard = document.createElement("div"); reportCard.id = "ce-live-report" reportCard.style.cssText = ` position: relative; width: 100%; top: 0; left: 0; background: white; border: 1px solid #ccc; padding: 30px; z-index: 9999; `; const sections = ["Options", "Sidebar", "DevTool Panel", "DevTool Sidebar"]; const ids = ["report-opt", "report-sp", "report-dvtp", "report-dvtsb"]; const checks = sections.map((section, index) => { return `
? ${section}
`; }); reportCard.innerHTML = `
Extension Status Report
${checks.join("")} `; document.body.prepend(reportCard); (async () => { const ids = ["report-opt", "report-sp", "report-dvtp", "report-dvtsb"]; const storageKeys = ["opt", "sp", "dvtp", "dvtsb"]; console.log({ids, storageKeys}); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const storageKey = storageKeys[i]; const el = document.getElementById(id); if (el) { const result = await chrome.storage.local.get([storageKey]); const keyExistsAndIsTrue = result && typeof result?.[storageKey] !== "undefined" && result[storageKey] === true; if (keyExistsAndIsTrue) { el.innerHTML = "✅"; } else { el.innerHTML = "❌"; } } // if } // for })(); } }); }); } // updateLiveReport chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "add-live-report") { updateLiveReport(); } }); ``` The function updateLiveReport() is called whenever you press the button “Add report card of check-ins to webpage” at the popup. The function look for the active tab, then deletes any old report, then adds a report (a for loop is used to easily create each status (Options, Sidebar, DevTool Panel, and DevTool Sidebar) while keeping code DRY). Then with a for loop, it renders each x-mark or checkmark depending at the statuses based on storage keys opt, sp, dvtp, dvtsb. Now whenever you open the popup and re-add a live report, it’ll give you updated statuses on which presentations have been ticked. Recall that each presentation (options.html, sidepanel.html, dvt-panel.html, dvt-sidebar.html) updates the Storage api for the keys opt, sp, dvtp, dvtsb). The behavior is such: ![[Chrome-extension-report-not-live.gif]] But this is super inconvenient to have to refresh the dashboard. What if the dashboard can change in real time based on the user ticking the check-in’s. ## Make the check-in report real time We start by adding this storage onchange handler to the end of background.js: ``` chrome.storage.onChanged.addListener(async(changes, namespace) => { // console.log({changes, namespace}); if(namespace === "local"){ updateLiveReport(); } // if }); ``` Now whenever you tick/untick in sidebar, it’ll automatically update the report at the top of your webpage. However, it won’t update if you tick/untick in the DevTools panel and DevTools sidebar. This is because background.js runs for sidebar and popup, and it scales down when not needed or at other presentations. When it scales down, certain features are disabled to preserve memory: A scaled down background js still listens for messages, but it does not run storage.onChange. What you need to do is add this to the bottom of devtools.js: ``` chrome.storage.onChanged.addListener((changes, namespace) => { console.log({changes, namespace}); if(namespace === "local"){ chrome.runtime.sendMessage({action: "add-live-report"}); } }); ``` ^ And this will at storage change because of devtools presentations (panel or sidebar), it’ll emit out `add-live-report`  message which is the same thing as clicking the button to add/refresh report at popup. Update the chrome extension and test it out at google.com. Make sure to open the popup first so you can add the report to google.com. You’ll find all tick/unticks are live, the report updating in real time on google.com: - However, you’ll find Options is not real time. The reason why Options is not real time is because it’s in a different tab, so when writing to the active tab for changes, it writes to the wrong tab (chrome extension setting tab) which doesn’t have the report dashboard. - For Options status to refresh, you have to re-add from the popup's button. ![[Chrome-extension-report-live.gif]] Troubleshooting: If the DevTools panels or sidebar ticks is not updating the dashboard in real time, did you open DevTools by right clicking inside the popup or sidebar? Make sure you right click the webpage at the tab (google.com) instead! Even though storage is scoped to the chrome extension itself and not the url you’re at, the Devtool’s onchange would be emitting `add-live-report`  to a background.js in the chrome extension presentation context rather than the website content presentation context, and you don’t have a website content there for the report dashboard to add to. As for the Options not live updating, there's an easy solution to that. Add to the bottom of background.js: ``` chrome.tabs.onActivated.addListener((tabId, tabStatus, tab) => { if (tabStatus === 'complete') { updateLiveReport(); } }); ``` ^ So when you switch from Options page back to google.com with the attached report dashboard, it re-renders and loads from the Options storage. ![[Chrome-extension-report-live-options.gif]]